<!--
@Description:远程搜索下拉框
@Author:zqy
@Date:2022-08-19 11:11:23
@Last Modified by:zqy
@Last Modified time:2022-08-19 11:11:23
 -->
<template>
  <el-select
    ref="customSelect"
    v-model="curSel"
    style="width:100%"
    :filterable="filterable"
    :clearable="clearable"
    :remote="remote"
    :disabled="disabled"
    :multiple="multiple"
    reserve-keyword
    :remote-method="remoteMethod"
    :loading="loading"
    :value-key="valueKey"
    v-bind="$attrs"
    @visible-change="isShowSelectOptions"
    @change="selectChange"
    @focus="onFocus()"
    @clear="remoteMethod('')"
    @blur="onBlur()"
  >
    <slot></slot>
    <el-option
      v-for="(item, index) in optionList"
      :key="`${item[valueName]}${index}${timer}`"
      :label="labelSet(item)"
      :value="item[valueName]"
    ></el-option>
  </el-select>
</template>
<script>
import request from '../../apis/axios'
export default {
  name: 'QupSearchSelectPicker',
  inheritAttrs: false,
  props: {
    value: {
      // 选中的值
      type: [Number, String, Array],
      default: ''
    },
    lable: {
      // 选中值的名称
      type: [String, Array],
      default: ''
    },
    currentId: {
      // 选中值的Id
      type: String,
      default: ''
    }, 
    curOptions: {
      // 当前选中选项，默认为对象，multiple为true时为数组
      type: [Array, Object],
      default: () => {
        return {}
      }
    },
    valueName: {
      // 选项值键名
      type: String,
      default: 'id'
    },
    lableName: {
      // 选项名称键名
      type: String,
      default: ''
    },
    valueKey: {
      // 作为 value 唯一标识的键名，绑定值为对象类型时必填
      type: String,
      default: 'id'
    },
    customName: {
      // 输入值的查询键名，有值时使用，否则使用lableName
      type: String,
      default: ''
    },
    customOperator: {
      // 输入值的查询键名匹配符，有值时使用，否则使用lableName
      type: String,
      default: 'like' // like 、 =
    },
    url: {
      // 查询地址
      type: String,
      default: ''
    },
    method: {
      // 请求方式 get/post,默认post
      type: String,
      default: 'post'
    },
    params: {
      // 请求参数,query为具体参数，type为传参方式（1 body方式,0 formData方式）
      type: Object,
      default: () => {
        return { query: null, type: 0 }
      }
    },
    paramsRaw: {
      // 标准分页查询额外参数(对应分页查询入参：paramsRaw)
      type: Object,
      default: () => ({})
    },
    parentId: {
      // 父级参数(父级Id或code)，改变时刷新列表
      type: [String, Number, Array],
      default: ''
    },
    mustPid: {
      // 是否必须有父级id
      type: Boolean,
      default: false
    },
    mustVal: {
      // 是否必须有值查询
      type: Boolean,
      default: false
    },
    disabled: {
      // 是否禁用	
      type: Boolean,
      default: false
    },
    clearable: {
      // 是否允许清除
      type: Boolean,
      default: true
    },
    multiple: {
      // 多选时value，lable必须为数组
      type: Boolean,
      default: false
    },
    filterable:{
      //是否可搜索(远程搜索)
      type:Boolean,
      default:true
    },
    remote:{
      //是否为远程搜索
      type:Boolean,
      default:true
    },
    labelFunc: {
      // 选项名称自定义回调方法
      type: Function,
      default: null
    },
    optionData: {
      // 选项列表数据，该数据不为空时，不请求远程搜索
      type: Array,
      default: undefined
    },
    reqHeaders:{
      // 请求头参数
      type: Object,
      default: null
    },
    includes: {
      // 包含指定的字典编码，若为Function则返回值需要是字典编码数组
      type: [Array,Function],
      default: undefined
    },
    excludes: {
      // 排除指定的字典编码，若为Function则返回值需要是字典编码数组
      type: [Array,Function],
      default: undefined
    }
  },
  data() {
    return {
      loading: false,
      curSel: '',
      tempLable: null, // 选中项的name值，没有绑定lable时使用
      timer:new Date().getTime(),
      list: [],
      exCodeList:[],
      inCodeList:[]
    }
  },
  watch: {
    value: {
      handler(val) {
        if (this.multiple) {
          this.curSel = []
         if (val && val instanceof Array) {
            val.forEach(e => {
              if(!(e===null || e===undefined || e==='')){
                this.curSel.push(e.toString())
              }
            })
          }
        } else {
          this.curSel =(val===null || val===undefined) ? '' : val.toString()
          if (!val) {
            this.tempLable = null
          }
          // this.remoteMethod()
        }
        // 数据请求未返回时，设置默认选项
        this.setList(this.curSel)
      },
      deep: true,
      immediate: true
    },
    url: {
      handler() {
        this.remoteMethod()
      },
      immediate: true
    },
    parentId: {
      // 监听parentId实现父级变化后刷新列表
      handler(val) {
        this.remoteMethod()
      }
    }
    // params: {
    //   handler() {
    //     this.remoteMethod()
    //   },
    //   deep: true
    // }
  },
  computed:{
    optionList() {
      let t = this.list
      if (this.inCodeList instanceof Array && this.inCodeList.length>0) {
        t = t.filter(e => this.inCodeList.some(v=>v.toString()===e[this.valueName].toString()))
      }
      if (this.exCodeList instanceof Array && this.exCodeList.length > 0) {
        t = t.filter(e =>!this.exCodeList.some(v=>v.toString()===e[this.valueName].toString()))
      }
      return t
    }
  },
  created() {
    this.getCludeList()
  },
  methods: {
    // 是否显示下拉框
    isShowSelectOptions(bol) {
      const thisNode = this.$refs.customSelect
      try {
        if (!bol) thisNode.blur()
        // 使远程搜索框可以选择
        if (bol && !this.multiple) {
          thisNode.selectedLabel = this.tempLable ? this.tempLable : this.lable
        }
      } catch (error) {
        console.log(error)
      }
    },
    // 选择变化
    selectChange(val) {
      if (this.multiple) {
        const nameList = []
        const ary = []
        const ids = []
        val.forEach(code => {
          const item = this.optionList.find(e => {
            return e[this.valueName].toString() === code.toString()
          })
          nameList.push(item[this.lableName])
          ary.push(item)
          if (item.id) {
            ids.push(item.id)
          }
        })
        this.$emit('update:lable', nameList)
        this.$emit('update:curOptions', ary)
        this.$emit('update:currentId', ids)
        this.$emit('change', ary)
      } else {
        const obj = this.optionList.find(e => {
          return e[this.valueName].toString() === val.toString()
        })
        const name = obj ? obj[this.lableName] : ''
        this.tempLable = name
        this.$emit('update:lable', name)
        this.$emit('update:curOptions', obj)
        this.$emit('update:currentId', obj ? obj.id : '')
        this.$emit('change', obj)
      }
      this.$emit('update:value', val)
    },
    setList(val) {
      this.timer = new Date().getTime()
      // 判断列表中是否有传入的选项
      if (this.multiple) {
        for (const i in val) {
          const bol = this.list.every(e => {
            return e[this.valueName].toString() !== val[i].toString()
          })
          // 没有当前选项则加上
          if (bol) {
            if (this.lable) {
              const d = {
                [this.lableName]: this.lable[i] || '',
                [this.valueName]: val[i]
              }
              this.list.unshift(d)
            } else {
              const d = {
                [this.lableName]: this.tempLable ? this.tempLable[i] || '' :'',
                [this.valueName]: val[i]
              }
              this.list.unshift(d)
            }
          }
        }
      } else {
        const bol = this.list.every(e => {
          return e[this.valueName].toString() !== val.toString()
        })
        // 没有当前选项则加上
        if (bol && this.value) {
          const d = {
            [this.lableName]: this.lable ? this.lable : this.tempLable || '',
            [this.valueName]: val
          }
          this.list.unshift(d)
        }
      }
    },
    remoteMethod(val) {
      if (this.optionData) {
        this.list = this.optionData
        this.setList(this.curSel)
        return
      }
      if (this.mustPid && !this.parentId) {
        this.list = []
        this.setList(this.curSel)
        return
      }
      if (this.mustVal && !val) {
        return
      }
      const formObj = new FormData()
      const filtersRaw = []
      const obj = { ...this.params.query }
      if (val) delete obj[this.customName]
      if (obj) {
        for (const i in obj) {
          if (Array.isArray(obj[i]) && obj[i].length === 2) {
            filtersRaw.push({
              id: Math.floor(Math.random() * 0xffffffffff).toString(16),
              value: obj[i][0],
              property: i,
              operator: '>='
            })
            filtersRaw.push({
              id: Math.floor(Math.random() * 0xffffffffff).toString(16),
              value: obj[i][1],
              property: i,
              operator: '<='
            })
          } else if (obj.hasOwnProperty(i) && Object.prototype.toString.call(obj[i]) === '[object Object]') {
            const v = obj[i]
            if (v.value instanceof Array) {
              if (v.value.length <= 0) {
                continue
              } else {
                v.value = `[${v.value.join(',')}]`
              }
            }
            filtersRaw.push({
              id: Math.floor(Math.random() * 0xffffffffff).toString(16),
              value: v.value,
              property: i,
              operator: v.operator
            })
          } else if (obj[i] !== '[]' && obj[i] !== '' && obj[i] !== undefined && obj[i] !== null) {
            filtersRaw.push({
              id: Math.floor(Math.random() * 0xffffffffff).toString(16),
              value: obj[i],
              property: i,
              operator: 'like'
            })
          }
        }
      }
      if (val) {
        filtersRaw.push({
          id: Math.floor(Math.random() * 0xffffffffff).toString(16),
          value: val,
          property: this.customName ? this.customName : this.lableName,
          operator: this.customName ? this.customOperator : 'like'
        })
      }
      const paramsRaw = {}
      for (const [k, v] of Object.entries(this.paramsRaw)) {
        if (v === '' || v === null || v === undefined) {
          continue
        }
        if (v instanceof Array) {
          const _v = v.filter(e => e !== '' && e !== null && e !== undefined)
          if (_v.length > 0) {
            // paramsRaw[k] = `[${_v.join(',')}]`
            paramsRaw[k] =_v
          }
        } else if (Object.prototype.toString.call(v) === '[object Object]') {
          paramsRaw[k] = JSON.stringify(v)
        }
      }
      formObj.append('page', 1)
      formObj.append('rows', 50)
      formObj.append('filtersRaw', JSON.stringify(filtersRaw))
      formObj.append('paramsRaw', JSON.stringify(paramsRaw))
      // const obj = this.params.query
      const strList = ['page=1', 'rows=50']
      let str = ''
      for (const i in obj) {
        if (obj[i]) {
          strList.push(i + '=' + obj[i])
        }
      }
      if (val) {
        strList.push(
          (this.customName ? this.customName : this.lableName) + '=' + val
        )
      }
      if (strList.length) {
        str = '?' + strList.join('&')
      }
      
      this.loading = true
      let reqConfig = {}
      if (this.method.toLowerCase() === 'get') {
        reqConfig = {
          url: this.url + str,
          method: this.method
        }
      }else{
        let reqData = ''
        if(this.params.type === 0){
          // 标准filter查询
          reqData = formObj 
        }else if(this.params.type === 3){
          // JSON数据
          reqData={...obj}
        }else if(this.params.type===2){
          // 普通formData
          reqData= new FormData()
          for (const key in obj) {
            reqData.append(key, obj[key])
          }
        }
        reqConfig = {
          url: this.url + (this.params.type === 1 ? str : ''),
          method: this.method,
          data:reqData
        }
      }
      if(this.reqHeaders && Object.keys(this.reqHeaders).length>0){
        reqConfig.headers = this.reqHeaders
      }

      this.getCludeList()

      request(reqConfig).then(res => {
        this.loading = false
        if (res.success === 1) {
          if (Object.keys(res.data).indexOf('records') === -1) {
            this.list = res.data.map(e => {
              e[this.valueName] = e[this.valueName].toString()
              return e
            })
          } else {
            this.list = res.data.records.map(e => {
              e[this.valueName] = e[this.valueName].toString()
              return e
            })
          }
          this.$emit('data-change',this.list)
          this.setList(this.curSel)
        }
      }).catch(er => {
        this.loading = false
      })
    },
    onFocus() {
      // 获取焦点刷新，建议删除（刷新数据太频繁，每次选择要等待数据刷新）
      // if (!this.multiple) this.remoteMethod();
      if (this.$listeners['on-focus'] instanceof Function) {
        this.$listeners['on-focus']()
      }
    },
    onBlur() {
      if (this.$listeners['on-blur'] instanceof Function) {
        this.$listeners['on-blur']()
      }
    },
    labelSet(item) {
      if (this.labelFunc) {
        return this.labelFunc(item)
      } else {
        return item[this.lableName]
      }
    },
    getCludeList(){
      let inCodeList = []
      if(this.includes){
        if(this.includes instanceof Array){
            inCodeList = this.includes
        }else if(this.includes instanceof Function){
          try {
            const result = this.includes()
            if (result instanceof Promise) {
              result.then((data) => {
                inCodeList = data instanceof Array ? data : []
              })
            } else {
              inCodeList = result instanceof Array ? result : []
            }
          } catch (e) {
            this.inCodeList = []
          }
        }
      }
      this.inCodeList = inCodeList

      let exCodeList = []
      if(this.excludes){
        if(this.excludes instanceof Array){
            exCodeList = this.excludes || []
        }else if(this.excludes instanceof Function){
          try {
            const result = this.excludes()
            if (result instanceof Promise) {
              result.then((data) => {
                exCodeList = data instanceof Array ? data : []
              })
            } else {
              exCodeList = result instanceof Array ? result : []
            }
          } catch (e) {
            exCodeList = []
          }
        }
      }
      this.exCodeList = exCodeList
    },
  }
}
</script>
<style lang="scss" scoped>
</style>
